package com.glodon.paas.account.web.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.glodon.paas.account.api.bean.Member;
import com.glodon.paas.account.api.bean.Org;
import com.glodon.paas.account.api.bean.User;
import com.glodon.paas.account.api.exception.DuplicateInviteException;
import com.glodon.paas.account.security.spring.LoginUser;
import com.glodon.paas.account.service.OrgService;
import com.glodon.paas.account.web.util.SecurityUtils;
import com.glodon.paas.exception.PaasException;
import com.glodon.paas.exception.PaasServerException;

@Controller
public class OrgController {
    @Autowired
    private OrgService orgService;

    @RequestMapping(value = "/org", method = RequestMethod.GET)
    public void list(Map<String, Object> model) throws PaasException {
        String userId = currentUserId();
        List<Org> orgs = orgService.getOrgs(userId);
        List<Map<String, Object>> orgTree = new ArrayList<Map<String, Object>>();
        for (Org org : orgs) {
            Map<String, Object> orgModel = convert(org, true);
            orgTree.add(orgModel);
        }
        if(orgs!=null&&!orgs.isEmpty()){
        	model.put("owner", orgs.get(0).getOwner());
        }else{
        	model.put("owner", "");
        }
        
        model.put("data", orgTree);
    }
    @RequestMapping(value = "/institution", method = RequestMethod.GET)
    public void listAll(Map<String, Object> model) throws PaasException {
        String userId = currentUserId();
        List<Org> orgs = orgService.getOrgs(userId);
        model.put("org", orgs);
    }
    @RequestMapping(value = "/org/{orgId}/path", method = RequestMethod.GET)
    public void getOrgPath(@PathVariable String orgId,Map<String, Object> model) throws PaasException {
        Org org=new Org();
        org= getParent(org,orgId);
        model.put("org", org);
    }
    private Org getParent(Org pathOrg,String orgId){
        Org org = orgService.getOrg(orgId);
        List<Org> subList = new ArrayList<Org>();
        if(org.getParentId()!=null&&!org.getParentId().isEmpty()){
            List<Org> subLst= new ArrayList<Org>();
            if(pathOrg.getId()==null){
                subLst.add(pathOrg);
            }
            org.setSubOrgs(subLst);
            org.setMembers(null);
            subList.add(org);
            Org parent = orgService.getOrg(org.getParentId());
            parent.setSubOrgs(subList);
            parent.setMembers(null);
            pathOrg =parent;
            if(parent.getParentId()!=null&&!parent.getParentId().isEmpty()){
                pathOrg=getParent(pathOrg,parent.getParentId()); 
            }
        }else{
            if(pathOrg.getId()!=null){
                subList.add(pathOrg);
            }
            org.setSubOrgs(subList);
            org.setMembers(null);
            pathOrg =org;
        }
        return pathOrg;
    }
    private Map<String, Object> convert(Org org, boolean topLevel) {
        Map<String, Object> orgModel = new HashMap<String, Object>();
        setTitle(orgModel, org.getName());
        setMetadata(orgModel, org.getId(), null, null);
        setRel(orgModel, org.getId(), topLevel ? "organization" : "institution");
        List<Org> subOrgs = org.getSubOrgs();
        if (subOrgs != null) {
            List<Map<String, Object>> children = new ArrayList<Map<String, Object>>();
            for (Org subOrg : subOrgs) {
                children.add(convert(subOrg, false));
            }

            //            List<String> members = org.getMembers();
            //            if (!members.isEmpty()) {
            //                for (String member : members) {
            //                    Map<String, Object> memberModel = new HashMap<String, Object>();
            //                    User user = userService.getUserById(member);
            //                    setTitle(memberModel, user.getDisplayName());
            //                    setMetadata(memberModel, user.getId(), user.getDisplayName(), user.getEmail());
            //                    setRel(memberModel, user.getId(), "user");
            //                    children.add(memberModel);
            //                }
            //            }
            orgModel.put("children", children);
        }
        return orgModel;
    }

    private void setRel(Map<String, Object> model, String id, String rel) {
        Map<String, String> attr = new HashMap<String, String>();
        attr.put("id", id);
        attr.put("rel", rel);
        model.put("attr", attr);
    }

    private void setMetadata(Map<String, Object> model, String id, String name, String email) {
        Map<String, String> idModel = new HashMap<String, String>();
        idModel.put("id", id);
        if (name != null)
            idModel.put("name", name);
        if (email != null)
            idModel.put("email", email);
        model.put("metadata", idModel);
    }

    private void setTitle(Map<String, Object> model, String title) {
        Map<String, String> titleModel = new HashMap<String, String>();
        titleModel.put("title", title);
        model.put("data", titleModel);
    }

    @RequestMapping(value = "/org/{orgId}", method = RequestMethod.GET)
    public void get(@PathVariable String orgId, Map<String, Object> model) throws PaasException {
        Org org = orgService.getOrg(orgId);
        String userId = currentUserId();
        if (!orgService.isMember(userId, org.getId()))
            throw new PaasException("current user [" + userId + "] is not in org[" + orgId + "]");
        model.put("org", org);
    }
    @RequestMapping(value = "/org/{orgId}/member", method = RequestMethod.GET)
    public void getOrgAndMembers(@PathVariable String orgId, Map<String, Object> model) throws PaasException {
        Org org = orgService.getOrg(orgId);
        List<Org> subOrg = orgService.getOrgAndMembers(orgId);
        org.setSubOrgs(subOrg);
        String userId = currentUserId();
        if (!orgService.isMember(userId, org.getId()))
            throw new PaasException("current user [" + userId + "] is not in org[" + orgId + "]");
        model.put("org", org);
    }
    @RequestMapping(value = "/institution/{parentId}", method = RequestMethod.POST)
    public void createSubOrg(@PathVariable String parentId, String name, Map<String, Object> model)
            throws PaasException {
        Org org = new Org();
        org.setName(name);
        org.setOwner(currentUserId());
        org.setParentId(parentId);
        Org parent = orgService.getOrg(parentId);
        checkOwner(parent);
        orgService.addOrg(org);
        model.put("org", org);
    }

    @RequestMapping(value = "/org", method = RequestMethod.POST)
    public void create(String name, Map<String, Object> model) throws PaasException {
        Org org = new Org();
        org.setName(name);
        org.setOwner(currentUserId());
        orgService.addOrg(org);
        model.put("org", org);
    }

    @RequestMapping(value = "/org/{orgId}", method = RequestMethod.DELETE)
    public void deleteOrg(@PathVariable String orgId, HttpServletRequest request) throws PaasException {
        Org org = orgService.getOrg(orgId);
        checkOwner(org);
        if(request.getParameter("force") != null)
        	orgService.deleteOrg(orgId, true);
        else
        	orgService.deleteOrg(orgId);
    }

    @RequestMapping(value = "/institution/{institutionId}", method = RequestMethod.DELETE)
    public void deleteInst(@PathVariable String institutionId, HttpServletRequest request) throws PaasException {
        deleteOrg(institutionId, request);
    }

    @RequestMapping(value = "/org/{orgId}", method = RequestMethod.PUT)
    public void update(@PathVariable String orgId, String parentId, String name, int seq, Map<String, Object> model)
            throws PaasException {
        Org org = orgService.getOrg(orgId);
        checkOwner(org);
        if (parentId != null)
            org.setParentId(parentId);
        org.setName(name);
        org.setSeq(seq);
        orgService.updateOrg(org);
        model.put("org", org);
    }

    @RequestMapping(value = "/institution/{institutionId}", method = RequestMethod.PUT)
    public void updateInst(@PathVariable String institutionId, String parentId, String name, int seq,
            Map<String, Object> model) throws PaasException {
        update(institutionId, parentId, name, seq, model);
    }

    @RequestMapping(value = "/institution/{institutionId}/member", method = RequestMethod.GET)
    public void getMembers(@PathVariable String institutionId, Map<String, Object> model) {
        Org org = orgService.getOrg(institutionId);
        model.put("members", org.getMembers());
    }

    @RequestMapping(value = "/institution/{institutionId}/invite/{email:.+}", method = RequestMethod.PUT)
    public void invite(@PathVariable String institutionId, @PathVariable String email) throws PaasServerException {
        Member member = orgService.getMember(institutionId, email);
        if(member == null) {
            orgService.addMemeber(institutionId, email);
        } else if(member.getStatus() != Member.ACCEPT) {
            orgService.sendInviteMail(member);
        } else {
            throw new DuplicateInviteException("5 minutes duplicate invite member");
        }
    }

    @RequestMapping(value = "/member/invite/{inviteCodeBase64}", method = RequestMethod.GET)
    public String invite(@PathVariable String inviteCodeBase64 ,  Map<String, Object> model) {
        model.put("inviteCode" , inviteCodeBase64);
        return "agree";
    }

    @RequestMapping(value = "/member/invite/{inviteCodeBase64}", method = RequestMethod.POST)
    public void accept(@PathVariable String inviteCodeBase64 ,  Map<String, Object> model) {
        String inviteCode = new String(Base64.decodeBase64(inviteCodeBase64));
        LoginUser user = SecurityUtils.getCurrentUser();
        String userId = user.getId();
        orgService.accept(userId, user.getEmail(), inviteCode);
        model.put("result" , "success");
    }
    
    @RequestMapping(value = "/member/invite/{inviteCodeBase64}", method = RequestMethod.DELETE)
    public void deny(@PathVariable String inviteCodeBase64 ,  Map<String, Object> model) {
        String inviteCode = new String(Base64.decodeBase64(inviteCodeBase64));
        LoginUser user = SecurityUtils.getCurrentUser();
        String userId = user.getId();
        orgService.accept(userId, user.getEmail(), inviteCode);
        model.put("result" , "success");
    }

    @RequestMapping(value = "/institution/{institutionId}/member", method = RequestMethod.POST)
    public void addMembers(@PathVariable String institutionId, @RequestParam String[] userIds, Map<String, Object> model)
            throws PaasException {
        Org org = orgService.getOrg(institutionId);
        checkOwner(org);
        orgService.addMemeber(institutionId, userIds);
        org = orgService.getOrg(institutionId);
        model.put("org", org);
    }

    @RequestMapping(value = "/institution/{institutionId}/member/{key:.+}", method = RequestMethod.DELETE)
    public void removeMember(@PathVariable String institutionId, @PathVariable String key) throws PaasException {
        Org org = orgService.getOrg(institutionId);
        checkOwner(org);
        orgService.removeMember(institutionId, key);
    }

    private void checkOwner(Org org) throws PaasException {
        String userId = currentUserId();
        if (!userId.equals(org.getOwner()))
            throw new PaasException("current user [" + userId + "] is not org[" + org.getId() + "]'s owner");
    }

    private String currentUserId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User user = (User) authentication.getPrincipal();
        return user.getId();
    }
}
